<?php

namespace Lib\Db;

use Lib\Base\Config;
use Lib\Base\Request;

class DbProxy extends Db
{
    public function __construct($config)
    {
        $conInfo = [];
        if (isset($config['DB_GROUP_0'])) {
            $conInfo['DB_GROUP_0'] = $config['DB_GROUP_0'];
        }
        $this->initConfig($conInfo);
        $this->initInstanceId($config);
        $this->connectDb();
    }

    public function initConfig($conInfo = [])
    {
        if (RUN_ENV == 'ulink') {
            $actId = '';
            $file = '';
            $actConfig = Config::getActConfig($actId, $file);
            if ($actConfig === false || empty($actConfig['DB_CFG']['instance']) || empty($actConfig['DB_CFG']['db_name'])) {
                //$this->writeLog('-904', '读取数据库配置文件出现异常，请检查配置文件是否存在。', $file);
                recordLog('读取数据库配置文件出现异常。', __FILE__, __LINE__, LP_ERROR);
            } else {
                $instanceArr = explode('|', $actConfig['DB_CFG']['instance']);
                $dbNameArr = explode('|', $actConfig['DB_CFG']['db_name']);

                if (count($instanceArr) == count($dbNameArr)) {

                    for ($i = 0; $i < count($instanceArr); $i++) {
                        $this->config[0][$i]['instanceName'] = $instanceArr[$i];
                        $this->config[0][$i]['dbName'] = $dbNameArr[$i];
                    }
                } else {
                }
            }
        } else {
            //内部开发，直接读取config.php配置文件的中db相关信息
            $this->getConfig($conInfo);
        }

        //读取数据库实例对应的proxy的IP，port信息
        global $DB_COMMON_CFG;
        if (empty($DB_COMMON_CFG)) {
            $DB_COMMON_CFG = parse_ini_file("/usr/local/commweb/cfg/CommConfig/commconf_php.cfg", true);
        }
        foreach ($this->config as $groupId => $groupInfo) {
            foreach ($groupInfo as $instanceId => $instanceInfo) {
                $instanceCfg = $DB_COMMON_CFG[$instanceInfo['instanceName']];

                $host = $instanceCfg["proxy_ip"];
                if (Request::serverParam('PROXYNAME')) {
                    $host = 'proxy-yxgw-comm';
                }
                $this->config[$groupId][$instanceId]['host'] = $host;
                $this->config[$groupId][$instanceId]['port'] = $instanceCfg["proxy_port"];
                $this->config[$groupId][$instanceId]['userName'] = '';
                $this->config[$groupId][$instanceId]['password'] = '';
                $this->config[$groupId][$instanceId]['dbName'] = $instanceInfo['dbName'];
            }
        }
    }

    public function connectDb()
    {
        $dbConfig = $this->config[$this->groupId][$this->instanceId];
        $this->dbLink = new \DBProxy($dbConfig['host'], $dbConfig['port'], $dbConfig['dbName']);
    }

    /**
     * 执行查询语句
     * @param string $sql 要执行的sql语句
     * @return array|bool 成功：返回数组['rows'=>'结果集行数', 'data'=>[['第一条记录数据集，字段名和字段值组成的键值对数组'], [第二条记录数据集],...]]
     */
    public function execQuery($sql)
    {
        $startTime = microtime(true);
        //$this->writeLog('0', 'debug', 'execQuery start time:' . $startTime . '秒');

        $result = array();
        $ret = $this->dbLink->exec_query($sql, $result, $this->timeout);

        $endTime = microtime(true);
        $costTime = round((($endTime - $startTime) * 1000), 3);
        //$this->writeLog('0', 'debug', 'execQuery end time:' . $endTime . '秒,耗时:' . $costTime . 'ms');

        //查询出现异常$ret < 0。查询成功：$ret >= 0
        if ($ret < 0) {
            if ($ret == '-1' || $ret == '-2') {
                $this->tglogReport($ret);
            }
            $this->writeLog($this->dbLink->get_err_no(), $this->dbLink->get_err_msg(), ['sql' => $sql, 'costTime' => $costTime . "ms", 'ret' => $ret, 'result' => $result]);
            return false;
        } else {
            $this->writeLog('0', 'success', ['sql' => $sql, 'costTime' => $costTime . "ms", 'ret' => $ret, 'result' => $result]);
            return ['rows' => $ret, 'data' => $result];
        }
    }

    /**
     * 执行 /update/insert/delete/replace 相关语句
     * @param string $sql 要执行的sql语句
     * @return int|bool 成功：返回影响行数（>0）或者自增ID。影响行数如果为0或者SQL执行失败返回false
     *
     */
    public function execUpdate($sql, $getId = 0)
    {
        //insert 如果没有自增，无论插入多少条，成功返回0，失败返回负数
        //insert 如果有自增，插入一条返回自增ID，插入多条返回第一条数据的自增ID
        //insert () values (),(),... 插入多条时，如果有任何一条不成功，则所有的不成功，返回小于0

        //replace 返回影响行数，比如插入2行（主键重复），返回4（先删2行，在插入2行）,插入带自增ID的列，也不会返回自增ID
        //replace 插入一条完全相同的数据，影响行数1，插入1条主键或者唯一索引相同，有其他不同值的，影响行数2
        //replace 不管是否带主键，总是返回影响行数

        //delete 返回影响的行数
        //update 返回影响行数

        //insert update 如果有自增ID：没有重复主键（唯一索引）情况下，插入成功，返回自增ID
        //              如果有自增ID：有重复主键，但是更新数据跟插入数据一样，返回0
        //              没有自增ID：返回结果跟insert 一致

        $startTime = microtime(true);
        //$this->writeLog('0', 'debug', 'execUpdate start time:' . $startTime . '秒');

        $ret = $this->dbLink->exec_update($sql, $this->timeout);

        $endTime = microtime(true);
        $costTime = round((($endTime - $startTime) * 1000), 3);
        //$this->writeLog('0', 'debug', 'execUpdate end time:' . $endTime . '秒,耗时:' . $costTime . 'ms');

        $sqlType = strtoupper(\explode(' ', $sql)[0]);

        //sql执行失败返回false
        if ($ret < 0) {
            $this->writeLog($this->dbLink->get_err_no(), $this->dbLink->get_err_msg(), ['sql' => $sql, 'costTime' => $costTime . "ms", 'result' => $ret]);

            if ($ret == '-1' || $ret == '-2') {
                $this->tglogReport($ret);
            }

            return false;
        } else {
            $this->writeLog('0', 'success', ['sql' => $sql, 'costTime' => $costTime . "ms", 'result' => $ret]);

            //sql执行成功
            if (in_array($sqlType, ['INSERT'])) {
                return $getId ? $ret : true;
            } else {
                if ($ret == 0) {
                    $this->writeLog('-900', 'The number of affected rows is 0', $sql);
                }
                return $ret == 0 ? false : $ret;
            }
        }
    }

    public function execTrans($sqlArr)
    {
        $startTime = microtime(true);
        //$this->writeLog('0', 'debug', 'execTrans start time:' . $startTime . '秒');

        $ret = $this->dbLink->exec_trans($sqlArr, $this->timeout);

        $endTime = microtime(true);
        $costTime = round((($endTime - $startTime) * 1000), 3);
        //$this->writeLog('0', 'debug', 'execTrans end time:' . $endTime . '秒,耗时:' . $costTime . 'ms');

        //$ret < 0 :sql执行失败或者SQL执行成功，但是有部分SQL语句影响行数为0
        if ($ret < 0) {
            $this->writeLog($this->dbLink->get_err_no(), $this->dbLink->get_err_msg(), ['sql' => $sqlArr, 'ret' => $ret, 'costTime' => $costTime . 'ms', 'result' => $ret]);

            if ($ret == '-1' || $ret == '-2') {
                $this->tglogReport($ret);
            }
            return false;
        } else {
            $this->writeLog('0', 'success', ['sql' => $sqlArr, 'costTime' => $costTime . "ms", 'ret' => $ret, 'result' => $ret]);
            return true;
        }
    }
}
